﻿using CID.Domain.Development;
using CID.Tests.Support;
using NUnit.Framework;
using CID.Domain.Exceptions;
using System.Linq;
using Rhino.Mocks;
using CID.Domain.Users;

namespace CID.Domain.Tests.Development
{
    [TestFixture]
    class ReleaseTests : BaseFunctionalTests
    {
        private const string Name = "Release Name";
        private const string Description = "Release Description";
        private readonly IUser Responsible = Users.StubsBuilder.BuildUserStub();
 
        private Release release;

        public override void SetUp()
        {
            base.SetUp();
            release = new Release(Name, Description, Responsible);
        }

        #region Constructor
        [Test]
        public void CreateRelease_DefaultStatusShouldBePlanned()
        {
            var release = new Release(Name, Description, Responsible);

            Assert.AreEqual(Name, release.Name);
            Assert.AreEqual(Description, release.Description);
            Assert.AreEqual(Responsible, release.Responsible);
            Assert.AreEqual(ReleaseStatus.Planned, release.Status);
            CollectionAssert.IsEmpty(release.Versions);
        }
        #endregion

        #region SetActive
        [Test]
        public void SetActive_ReleaseInPlannedStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Planned);

            release.SetActive();

            Assert.AreEqual(ReleaseStatus.Active, release.Status);
        }

        [Test]
        public void SetActive_ReleaseInActiveStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Active);

            release.SetActive();

            Assert.AreEqual(ReleaseStatus.Active, release.Status);
        }

        [Test]
        public void SetActive_ReleaseInObsoleteStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Obsolete);

            release.SetActive();

            Assert.AreEqual(ReleaseStatus.Active, release.Status);
        }

        [Test, ExpectedException(typeof(InvalidActionForReleaseStatusException))]
        public void SetActive_ReleaseInReleasedStatus_ShouldThrowException()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Released);

            release.SetActive();
        }
        #endregion

        #region SetObsolete
        [Test]
        public void SetObsolete_ReleaseInPlannedStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Planned);

            release.SetObsolete();

            Assert.AreEqual(ReleaseStatus.Obsolete, release.Status);
        }

        [Test]
        public void SetObsolete_ReleaseInActiveStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Active);

            release.SetObsolete();

            Assert.AreEqual(ReleaseStatus.Obsolete, release.Status);
        }

        [Test]
        public void SetObsolete_ReleaseInObsoleteStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Obsolete);

            release.SetObsolete();

            Assert.AreEqual(ReleaseStatus.Obsolete, release.Status);
        }

        [Test, ExpectedException(typeof(InvalidActionForReleaseStatusException))]
        public void SetObsolete_ReleaseInReleasedStatus_ShouldThrowException()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Released);

            release.SetObsolete();
        }
        #endregion

        #region SetReleased
        [Test, ExpectedException(typeof(InvalidActionForReleaseStatusException))]
        public void SetReleased_ReleaseInPlannedStatus_ShouldThrowException()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Planned);

            release.SetReleased();
        }

        [Test]
        public void SetReleased_ReleaseInActiveStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Active);

            release.SetReleased();

            Assert.AreEqual(ReleaseStatus.Released, release.Status);
        }

        [Test, ExpectedException(typeof(InvalidActionForReleaseStatusException))]
        public void SetReleased_ReleaseInObsoleteStatus_ShouldThrowException()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Obsolete);

            release.SetReleased();
        }

        [Test]
        public void SetReleased_ReleaseInReleasedStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Released);

            release.SetReleased();

            Assert.AreEqual(ReleaseStatus.Released, release.Status);
        }
        #endregion

        #region AddVersion
        [Test]
        public void AddVersion_ReleaseInPlannedStatus_ShouldSucceed() 
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Planned);
            var version = StubsBuilder.BuildVersionStub();

            release.AddVersion(version);

            Assert.AreEqual(1, release.Versions.Count());
            CollectionAssert.Contains(release.Versions, version);
            version.AssertWasCalled(x => x.SetRelease(release));
        }

        [Test]
        public void AddVersion_ReleaseInActiveStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Active);
            var version = StubsBuilder.BuildVersionStub();

            release.AddVersion(version);

            Assert.AreEqual(1, release.Versions.Count());
            CollectionAssert.Contains(release.Versions, version);
            version.AssertWasCalled(x => x.SetRelease(release));
        }

        [Test, ExpectedException(typeof(InvalidActionForReleaseStatusException))]
        public void AddVersion_ReleaseInObsoleteStatus_ShouldThrowException()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Obsolete);
            var version = StubsBuilder.BuildVersionStub();

            release.AddVersion(version);
        }

        [Test, ExpectedException(typeof(InvalidActionForReleaseStatusException))]
        public void AddVersion_ReleaseInReleasedStatus_ShouldSucceed()
        {
            release.SetProperty(x => x.Status, ReleaseStatus.Released);
            var version = StubsBuilder.BuildVersionStub();

            release.AddVersion(version);
        }

        [Test]
        public void AddVersionTwice_OnlyOneShouldBeAdded()
        {
            var version = StubsBuilder.BuildVersionStub();

            release.AddVersion(version);
            release.AddVersion(version);

            Assert.AreEqual(1, release.Versions.Count());
            CollectionAssert.Contains(release.Versions, version);
        }
        #endregion

        #region RemoveVersion
        [Test]
        public void RemoveVersion_ReleaseInPlannedStatus_ShouldSucceed()
        {
            var version = StubsBuilder.BuildVersionStub(release: release);
            release.AddVersion(version);
            release.SetProperty(x => x.Status, ReleaseStatus.Planned);

            release.RemoveVersion(version);

            Assert.AreEqual(0, release.Versions.Count());
            CollectionAssert.DoesNotContain(release.Versions, version);
            version.AssertWasCalled(x => x.ResetRelease());
        }

        [Test]
        public void RemoveVersion_ReleaseInActiveStatus_ShouldSucceed()
        {
            var version = StubsBuilder.BuildVersionStub(release: release);
            release.AddVersion(version);
            release.SetProperty(x => x.Status, ReleaseStatus.Active);

            release.RemoveVersion(version);

            Assert.AreEqual(0, release.Versions.Count());
            CollectionAssert.DoesNotContain(release.Versions, version);
            version.AssertWasCalled(x => x.ResetRelease());
        }

        [Test]
        public void RemoveVersion_ReleaseInObsoleteStatus_ShouldSucceed()
        {
            var version = StubsBuilder.BuildVersionStub(release: release);
            release.AddVersion(version);
            release.SetProperty(x => x.Status, ReleaseStatus.Obsolete);

            release.RemoveVersion(version);

            Assert.AreEqual(0, release.Versions.Count());
            CollectionAssert.DoesNotContain(release.Versions, version);
            version.AssertWasCalled(x => x.ResetRelease());
        }

        [Test, ExpectedException(typeof(InvalidActionForReleaseStatusException))]
        public void RemoveVersion_ReleaseInReleasedStatus_ShouldThrowException()
        {
            var version = StubsBuilder.BuildVersionStub(release: release);
            release.AddVersion(version);
            release.SetProperty(x => x.Status, ReleaseStatus.Released);

            release.RemoveVersion(version);
        }

        [Test]
        public void RemoveVersionTwice_ShouldSucceed()
        {
            var version = StubsBuilder.BuildVersionStub(release: release);
            release.AddVersion(version);

            release.RemoveVersion(version);
            release.RemoveVersion(version);

            Assert.AreEqual(0, release.Versions.Count());
            CollectionAssert.DoesNotContain(release.Versions, version);
        }

        [Test]
        public void RemoveVersion_VersionFromDifferentRelease_NothingShouldBeDone()
        {
            var version = StubsBuilder.BuildVersionStub();
            release.AddVersion(version);
            var anotherRelease = new Release("Another Release", Description, Responsible);
            var anotherVersion = StubsBuilder.BuildVersionStub(release: anotherRelease);
            anotherRelease.AddVersion(anotherVersion);

            release.RemoveVersion(anotherVersion);

            Assert.AreEqual(1, release.Versions.Count());
            CollectionAssert.Contains(release.Versions, version);
            Assert.AreEqual(1, anotherRelease.Versions.Count());
            anotherVersion.AssertWasNotCalled(x => x.ResetRelease());
        }
        #endregion
    }
}
